import ast
from typing import Dict, List, Any, Optional
import re
from loguru import logger

class CodeAnalyzer:
    """Code analysis tool for understanding and modifying code."""

    def __init__(self):
        self.parsers = {
            'python': self._analyze_python,
            'javascript': self._analyze_javascript,
            'java': self._analyze_java
        }

    def analyze(self, code: str, language: str = 'python') -> Dict[str, Any]:
        """Analyze code and return insights."""
        try:
            if language.lower() not in self.parsers:
                raise ValueError(f"Unsupported language: {language}")

            return self.parsers[language.lower()](code)
        except Exception as e:
            logger.error(f"Code analysis failed: {str(e)}")
            return {"error": str(e)}

    def _analyze_python(self, code: str) -> Dict[str, Any]:
        """Analyze Python code."""
        try:
            tree = ast.parse(code)
            
            analysis = {
                "imports": self._extract_python_imports(tree),
                "functions": self._extract_python_functions(tree),
                "classes": self._extract_python_classes(tree),
                "variables": self._extract_python_variables(tree),
                "complexity": self._calculate_python_complexity(tree),
                "issues": self._find_python_issues(tree, code)
            }
            
            return analysis
        except Exception as e:
            logger.error(f"Python code analysis failed: {str(e)}")
            return {"error": str(e)}

    def _extract_python_imports(self, tree: ast.AST) -> List[Dict[str, str]]:
        """Extract import statements from Python code."""
        imports = []
        for node in ast.walk(tree):
            if isinstance(node, ast.Import):
                for name in node.names:
                    imports.append({
                        "type": "import",
                        "name": name.name,
                        "alias": name.asname
                    })
            elif isinstance(node, ast.ImportFrom):
                for name in node.names:
                    imports.append({
                        "type": "from_import",
                        "module": node.module,
                        "name": name.name,
                        "alias": name.asname
                    })
        return imports

    def _extract_python_functions(self, tree: ast.AST) -> List[Dict[str, Any]]:
        """Extract function definitions from Python code."""
        functions = []
        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                functions.append({
                    "name": node.name,
                    "args": self._get_function_args(node),
                    "decorators": [self._get_decorator_name(d) for d in node.decorator_list],
                    "docstring": ast.get_docstring(node),
                    "line_number": node.lineno
                })
        return functions

    def _extract_python_classes(self, tree: ast.AST) -> List[Dict[str, Any]]:
        """Extract class definitions from Python code."""
        classes = []
        for node in ast.walk(tree):
            if isinstance(node, ast.ClassDef):
                classes.append({
                    "name": node.name,
                    "bases": [self._get_base_class_name(base) for base in node.bases],
                    "methods": self._get_class_methods(node),
                    "docstring": ast.get_docstring(node),
                    "line_number": node.lineno
                })
        return classes

    def _extract_python_variables(self, tree: ast.AST) -> List[Dict[str, Any]]:
        """Extract variable assignments from Python code."""
        variables = []
        for node in ast.walk(tree):
            if isinstance(node, ast.Assign):
                for target in node.targets:
                    if isinstance(target, ast.Name):
                        variables.append({
                            "name": target.id,
                            "line_number": target.lineno
                        })
        return variables

    def _calculate_python_complexity(self, tree: ast.AST) -> Dict[str, int]:
        """Calculate code complexity metrics."""
        complexity = {
            "cyclomatic_complexity": 0,
            "number_of_functions": 0,
            "number_of_classes": 0,
            "number_of_lines": 0
        }
        
        for node in ast.walk(tree):
            if isinstance(node, (ast.If, ast.While, ast.For, ast.FunctionDef, ast.AsyncFunctionDef)):
                complexity["cyclomatic_complexity"] += 1
            elif isinstance(node, ast.FunctionDef):
                complexity["number_of_functions"] += 1
            elif isinstance(node, ast.ClassDef):
                complexity["number_of_classes"] += 1
        
        return complexity

    def _find_python_issues(self, tree: ast.AST, code: str) -> List[Dict[str, Any]]:
        """Find potential issues in Python code."""
        issues = []
        
        # Check for long lines
        lines = code.split('\n')
        for i, line in enumerate(lines, 1):
            if len(line) > 79:  # PEP 8 line length limit
                issues.append({
                    "type": "style",
                    "message": "Line too long",
                    "line_number": i
                })
        
        # Check for complex functions
        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                complexity = 1
                for child in ast.walk(node):
                    if isinstance(child, (ast.If, ast.While, ast.For)):
                        complexity += 1
                if complexity > 10:  # Arbitrary threshold
                    issues.append({
                        "type": "complexity",
                        "message": f"Function '{node.name}' is too complex",
                        "line_number": node.lineno
                    })
        
        return issues

    def _get_function_args(self, node: ast.FunctionDef) -> Dict[str, List[str]]:
        """Extract function arguments."""
        args = {
            "positional": [],
            "keyword": [],
            "varargs": None,
            "kwargs": None
        }
        
        for arg in node.args.args:
            args["positional"].append(arg.arg)
        
        for arg in node.args.kwonlyargs:
            args["keyword"].append(arg.arg)
        
        if node.args.vararg:
            args["varargs"] = node.args.vararg.arg
        
        if node.args.kwarg:
            args["kwargs"] = node.args.kwarg.arg
        
        return args

    def _get_decorator_name(self, decorator: ast.expr) -> str:
        """Get decorator name from node."""
        if isinstance(decorator, ast.Name):
            return decorator.id
        elif isinstance(decorator, ast.Call):
            if isinstance(decorator.func, ast.Name):
                return decorator.func.id
        return "unknown"

    def _get_base_class_name(self, node: ast.expr) -> str:
        """Get base class name from node."""
        if isinstance(node, ast.Name):
            return node.id
        elif isinstance(node, ast.Attribute):
            return f"{self._get_base_class_name(node.value)}.{node.attr}"
        return "unknown"

    def _get_class_methods(self, node: ast.ClassDef) -> List[Dict[str, Any]]:
        """Get class methods."""
        methods = []
        for child in node.body:
            if isinstance(child, ast.FunctionDef):
                methods.append({
                    "name": child.name,
                    "args": self._get_function_args(child),
                    "decorators": [self._get_decorator_name(d) for d in child.decorator_list],
                    "docstring": ast.get_docstring(child),
                    "line_number": child.lineno
                })
        return methods

    def _analyze_javascript(self, code: str) -> Dict[str, Any]:
        """Analyze JavaScript code."""
        # Implement JavaScript analysis
        pass

    def _analyze_java(self, code: str) -> Dict[str, Any]:
        """Analyze Java code."""
        # Implement Java analysis
        pass 